package com.zx.framework.core.util;

import com.zx.framework.core.exception.FrameworkException;
import net.sf.sevenzipjbinding.*;
import net.sf.sevenzipjbinding.impl.RandomAccessFileInStream;
import net.sf.sevenzipjbinding.simple.ISimpleInArchive;
import net.sf.sevenzipjbinding.simple.ISimpleInArchiveItem;
import oracle.core.lmx.CoreException;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.apache.tools.zip.ZipEntry;
import org.apache.tools.zip.ZipOutputStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.zip.ZipFile;

public class FileUtil extends FileUtils {
    private static Logger LOG = LoggerFactory.getLogger(FileUtil.class);

    /**
     * 获取扩展名
     *
     * @param fileName
     * @return
     */
    public static String getFileExtension(String fileName) {
        return FilenameUtils.getExtension(fileName);
    }

    /**
     * 获取
     *
     * @param fileName
     * @return
     */
    public static String getFileWithoutExtension(String fileName) {
        return FilenameUtils.getBaseName(fileName);
    }

    /**
     * 压缩文件
     *
     * @param srcFile
     * @param os
     */
    public static void zip(File srcFile, ZipOutputStream os, String base) {
        File[] files = null;
        try {
            if (srcFile.isDirectory()) {
                base = StringUtil.isEmpty(base) ? "" : base + "/";
                files = srcFile.listFiles();
                for (File file : files) {
                    zip(file, os, base + file.getName());
                }
            } else {
                os.putNextEntry(new ZipEntry(base));
                BufferedInputStream bin = null;
                try {
                    bin = new BufferedInputStream(new FileInputStream(srcFile));
                    byte[] byteArray = new byte[1024];
                    int len = 0;
                    while ((len = bin.read(byteArray)) != -1) {
                        os.write(byteArray, 0, len);
                    }
                    os.setEncoding(System.getProperty("file.encoding"));
                } catch (Exception e) {
                    e.printStackTrace();
                    throw new CoreException();
                } finally {
                    IOUtils.closeQuietly(bin);
                }
            }
        } catch (Exception e) {
            IOUtils.closeQuietly(os);
            throw new FrameworkException("文件压缩失败！");
        }
    }
    
    /**
     * 压缩文件
     *
     * @param srcFile
     * @param descFile
     */
    public static void zip(String srcFile, String descFile) {
        ZipOutputStream os = null;
        try {
            File temp = new File(srcFile);
            os = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(descFile)));
            zip(new File(srcFile), os, temp.getName());
        } catch (Exception e) {
            e.printStackTrace();
            throw new FrameworkException("文件压缩失败！");
        } finally {
            IOUtils.closeQuietly(os);
        }
    }

    /**
     * 使用源文件所在路径原地解压<br/>
     * 使用7-Zip-JBinding解压,7zip的解压算法,效率更高,支持几乎所有常见压缩文件类型<br/>
     * 支持类型 7z Zip Rar Tar Split Lzma Iso HFS GZip Cpio BZip2 Z Arj Chm Lhz Cab Nsis Deb Rpm Wim Udf
     *
     * @param srcFile 源文件位置
     */
    public static void extract(String srcFile){
        extract(srcFile,null);
    }

    /**
     * 将源文件解压到指定路径下<br/>
     * 使用7-Zip-JBinding解压,7zip的解压算法,效率更高,支持几乎所有常见压缩文件类型<br/>
     * 支持类型 7z Zip Rar Tar Split Lzma Iso HFS GZip Cpio BZip2 Z Arj Chm Lhz Cab Nsis Deb Rpm Wim Udf
     *
     * @param srcFile 源文件位置
     * @param descPath 解压目标路径,目标路径为空时使用源文件所在路径原地解压
     */
    public static void extract(String srcFile,String descPath) {
        RandomAccessFile randomAccessFile = null;
        IInArchive inArchive = null;
        final String path = StringUtil.isNotBlank(descPath)?descPath:FilenameUtils.getFullPath(srcFile);
        try {
//            SevenZip.initSevenZipFromPlatformJAR();
            randomAccessFile = new RandomAccessFile(srcFile, "r");
            inArchive = SevenZip.openInArchive(null, // autodetect archive type
                    new RandomAccessFileInStream(randomAccessFile));

            // Getting simple interface of the archive inArchive
            ISimpleInArchive simpleInArchive = inArchive.getSimpleInterface();

            LOG.debug("  Hash  |  Size  | Filename");
            LOG.debug("----------+------------+---------");

            for (final ISimpleInArchiveItem item : simpleInArchive.getArchiveItems()) {
                final int[] hash = new int[]{0};
                if (!item.isFolder()) {
                    String itemPath = FilenameUtils.getFullPath(path+item.getPath());
                    FileUtil.createDir(itemPath);
                    ExtractOperationResult result;
                    final long[] sizeArray = new long[1];
                    result = item.extractSlow(new ISequentialOutStream() {
                        public int write(byte[] data) throws SevenZipException {
                            //Write to file
                            FileOutputStream fos;
                            try {
                                File file = new File(path+item.getPath());
                                //error occours below
//                 file.getParentFile().mkdirs();
                                fos = new FileOutputStream(file);
                                fos.write(data);
                                fos.close();
                            } catch (IOException e) {
                                LOG.error("解压文件输出失败:"+e.getMessage(),e);
                                throw new FrameworkException("解压文件输出失败:",e);
                            }

                            hash[0] ^= Arrays.hashCode(data); // Consume data
                            sizeArray[0] += data.length;
                            return data.length; // Return amount of consumed data
                        }
                    });
                    if (result == ExtractOperationResult.OK) {
                        LOG.debug(String.format("%9X | %10s | %s", //
                                hash[0], sizeArray[0], item.getPath()));
                    } else {
                        LOG.error("Error extracting item: " + result);
                        throw new FrameworkException("解压文件内项目失败:"+result);
                    }
                }else {
                    FileUtil.createDir(path+item.getPath());
                }
            }
        } catch (Exception e) {
            LOG.error("解压文件失败:"+e.getMessage(),e);
            throw new FrameworkException("解压文件失败:"+e.getMessage(),e);
        } finally {
            if (inArchive != null) {
                try {
                    inArchive.close();
                } catch (SevenZipException e) {
                    LOG.error("Error closing archive: " + e,e);
                }
            }
            if (randomAccessFile != null) {
                try {
                    randomAccessFile.close();
                } catch (IOException e) {
                    LOG.error("Error closing file: " + e,e);
                }
            }
        }
    }

    /**
     * 使用javazip来解压
     *
     * @param srcZip
     * @param descFile
     * @throws IOException
     */
    public static void upzip(File srcZip, File descFile) throws IOException {
        int buffer = 512;
        BufferedOutputStream bos = null;
        if (descFile == null) {
            descFile = new File(srcZip.getParentFile().getAbsolutePath() + getFileWithoutExtension(srcZip.getName()));
            if (descFile.exists()) {
                deleteDirectory(descFile);
            }
            descFile.mkdirs();
        }
        ZipFile zipFile = null;
        try {

            zipFile = new ZipFile(srcZip);
            java.util.zip.ZipEntry entry = null;
            Enumeration<? extends java.util.zip.ZipEntry> enu = zipFile.entries();
            while (enu.hasMoreElements()) {
                entry = (java.util.zip.ZipEntry) enu.nextElement();
                File f = new File(descFile.getAbsolutePath() + entry.getName());
                if (!entry.isDirectory()) {
                    f.createNewFile();
                    BufferedInputStream bis = null;
                    try {
                        bos = new BufferedOutputStream(new FileOutputStream(f), buffer);
                        bis = new BufferedInputStream(zipFile.getInputStream(entry));

                        int count = 0;
                        byte[] array = new byte[buffer];
                        while ((count = bis.read(array, 0, buffer)) != -1) {
                            bos.write(array, 0, count);
                        }
                        bos.flush();
                    } catch (Exception e) {

                        e.printStackTrace();
                    } finally {

                        IOUtils.closeQuietly(bos);

                        IOUtils.closeQuietly(bis);
                    }
                } else {
                    f.mkdirs();
                }

            }
        } catch (Exception e) {

            e.printStackTrace();
            throw new FrameworkException("解压文件失败！");
        } finally {
            if (zipFile != null) {
                try {
                    zipFile.close();
                } catch (IOException e) {

                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 判断是否根驱动器
     *
     * @param path
     * @return
     */
    public static boolean isRoot(String path) {
        File[] tempRoot = File.listRoots();
        for (int i = 0; i < tempRoot.length; i++) {
            File f = tempRoot[i];
            if (path.equals(f.getAbsolutePath())) {
                return true;
            }
        }
        return false;
    }

    /**
     * 获取文件大小显示
     *
     * @param filesize
     * @return
     */
    public static String getShowSize(long filesize) {
        if (filesize >= 1024 * 1024) {
            return Math.floor(filesize / 1024 / 1024) + "MB";
        } else if (filesize >= 1024) {
            return Math.floor(filesize / 1024) + "KB";
        } else if (filesize >= 1024 * 1024 * 1024) {
            return Math.floor(filesize / 1024 / 1024 / 1024) + "GB";
        } else {
            return Math.floor(filesize) + "Bytes";
        }
    }

    public static void splitFileBySize(File file, File savePath, long size) throws CoreException {
        if (!file.exists() || (!file.isFile())) {
            throw new FrameworkException("file not exists！");
        }
        if (!savePath.exists()) {
            savePath.mkdirs();
        }
        if (size < 1) {
            throw new FrameworkException("split size can not less then 1！");
        }

        long fileLength = file.length();
        LOG.debug("file size:" + fileLength);

        // 取得被分割后的小文件的数目
        int num = (fileLength % size != 0) ? (int) (fileLength / size + 1)
                : (int) (fileLength / size);
        // 存放被分割后的小文件名
        // 输入文件流，即被分割的文件

        try {
            FileInputStream in = new FileInputStream(file);
            // 读输入文件流的开始和结束下标
            long end = 0;
            int begin = 0;
            // 根据要分割的数目输出文件
            for (int i = 0; i < num; i++) {
                // 对于前num – 1个小文件，大小都为指定的size
                File outFile = new File(savePath, file.getName() + i);
                // 构建小文件的输出流
                FileOutputStream out = new FileOutputStream(outFile);
                // 将结束下标后移size
                end += size;
                end = (end > fileLength) ? fileLength : end;
                // 从输入流中读取字节存储到输出流中
                for (; begin < end; begin++) {
                    out.write(in.read());
                }
                out.close();
            }
            in.close();
        } catch (FileNotFoundException e) {
            LOG.warn("file split file not found exception!");
            throw new FrameworkException("input or output file not found exception！");
        } catch (IOException e) {
            LOG.warn("file split I/O exception!");
            throw new FrameworkException("input or output file not found exception！");
        }
    }

    /**
     * Reads the contents of a file into a base64 string.
     * The file is always closed.
     *
     * @param file the file to read, must not be {@code null}
     * @return the file contents, never {@code null}
     * @throws IOException in case of an I/O error
     * @since 1.1
     */
    public String readFileToBase64(File file) throws IOException {
        return new String(Base64.encodeBase64(FileUtil.readFileToByteArray(file), true));
    }

    /**
     * Writes a String to a file creating the file if it does not exist using the default encoding for the VM.
     *
     * @param file   the file to write
     * @param base64 the content to write to the file
     * @throws IOException in case of an I/O error
     */
    public void writeBase64ToFile(File file, String base64) throws IOException {
        FileUtil.writeByteArrayToFile(file, Base64.decodeBase64(base64), false);
    }

    /**
     * 创建文件
     *
     * @param destFileName
     * @return
     */
    public static boolean createFile(String destFileName) {
        File file = new File(destFileName);
        if (file.exists()) {
            System.out.println("创建单个文件" + destFileName + "失败，目标文件已存在！");
            return false;
        }
        if (destFileName.endsWith(File.separator)) {
            System.out.println("创建单个文件" + destFileName + "失败，目标文件不能为目录！");
            return false;
        }
        //判断目标文件所在的目录是否存在
        if (!file.getParentFile().exists()) {
            //如果目标文件所在的目录不存在，则创建父目录
            System.out.println("目标文件所在目录不存在，准备创建它！");
            if (!file.getParentFile().mkdirs()) {
                System.out.println("创建目标文件所在目录失败！");
                return false;
            }
        }
        //创建目标文件
        try {
            if (file.createNewFile()) {
                System.out.println("创建单个文件" + destFileName + "成功！");
                return true;
            } else {
                System.out.println("创建单个文件" + destFileName + "失败！");
                return false;
            }
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("创建单个文件" + destFileName + "失败！" + e.getMessage());
            return false;
        }
    }

    /**
     * 创建目录
     *
     * @param destDirName
     * @return
     */
    public static boolean createDir(String destDirName) {
        File dir = new File(destDirName);
        return dir.mkdirs();
    }

    /**
     * 创建临时文件
     *
     * @param prefix
     * @param suffix
     * @param dirName
     * @return
     */
    public static String createTempFile(String prefix, String suffix, String dirName) {
        File tempFile = null;
        if (dirName == null) {
            try {
                //在默认文件夹下创建临时文件
                tempFile = File.createTempFile(prefix, suffix);
                //返回临时文件的路径
                return tempFile.getCanonicalPath();
            } catch (IOException e) {
                e.printStackTrace();
                throw new FrameworkException("创建临时文件失败！" + e.getMessage());
            }
        } else {
            File dir = new File(dirName);
            //如果临时文件所在目录不存在，首先创建
            if (!dir.exists()) {
                if (!FileUtil.createDir(dirName)) {
                    throw new FrameworkException("创建临时文件失败，不能创建临时文件所在的目录！");

                }
            }
            try {
                //在指定目录下创建临时文件
                tempFile = File.createTempFile(prefix, suffix, dir);
                return tempFile.getCanonicalPath();
            } catch (IOException e) {
                e.printStackTrace();
                throw new FrameworkException("创建临时文件失败！" + e.getMessage());
            }
        }
    }
}
